const QUEUE_LEN = 10;
const SIZE = 1 * 1024 * 1024;
uploadBtn.disabled = true;
const progressBar = document.querySelector('#progress');

function getCurrentProgress(array) {
    let res = 0;
    array.forEach(value => {
        res += value;
    });
    return res;
}
function handleProgress(index) {
    return function (progress) {
        container.uploaded[index] = progress.loaded;
        const upload = getCurrentProgress(container.uploaded);
        const currentProgress = (upload / container.file.size) * 100;
        progressBar.value = currentProgress;
    };
}
const container = {
    file: null,
    chunks: [],
    uploaded: []
};
function handleFileChange(e) {
    const [file] = e.target.files;
    if (!file) return;
    container.file = file;
    uploadBtn.disabled = false;
}
async function uploadChunks() {
    const limit = pLimit(QUEUE_LEN);
    const requestList = container.chunks
        .map(({file, hash, chunkName}, index) => {
            const formData = new FormData();
            formData.append('chunk-' + index, file);
            formData.append('hash', hash);
            formData.append('chunkName', chunkName);
            return formData;
        })
        .map((formData, index) =>
            limit(() =>
                axios
                    .post('/upload', formData, {
                        headers: {
                            'Content-Type': 'multipart/form-data'
                        },
                        onUploadProgress: handleProgress(index)
                    })
                    .catch(() =>
                        axios.post('/upload', formData, {
                            headers: {
                                'Content-Type': 'multipart/form-data'
                            },
                            onUploadProgress: handleProgress(index)
                        })
                    )
            )
        );
    await Promise.all(requestList);
}
function computeHash(chunks) {
    return new Promise(resolve => {
        container.worker = new Worker('/hash.js');
        container.worker.postMessage({chunks});
        container.worker.onmessage = e => {
            const {percentage, hash, index, chunkHash} = e.data;
            hashProgress.value = percentage;
            chunks[index].hash = chunkHash;
            if (hash) {
                resolve(hash);
            }
        };
        container.worker.onmessageerror = e => {
            reject(e);
        };
    });
}
function getFileBuffer(file) {
    const reader = new FileReader();
    reader.readAsArrayBuffer(file);
    return new Promise((resolve, reject) => {
        reader.onload = e => {
            resolve(e.target.result);
        };
        reader.onerror = e => {
            reject(e);
        };
    });
}
function hashChunks(chunks) {
    return Promise.all(
        chunks.map(async chunk => {
            const chunkBuffer = await getFileBuffer(chunk.file);
            chunk.hash = SparkMD5.ArrayBuffer.hash(chunkBuffer);
            return chunk;
        })
    );
}
function checkRequest(hash) {
    return axios.post('/check', {hash});
}
function reset() {
    progressBar.value = 0;
    hashProgress.value = 0;
    uploadBtn.disabled = true;
    container.uploaded = [];
}
async function handleUpload(e) {
    uploadBtn.disabled = true;
    const fileChunkList = createFileChunk(container.file);
    container.chunks = fileChunkList.map(({file}, index) => ({
        file,
        chunkName: index + '-' + container.file.name
    }));
    // const [_, hash] = await Promise.all([
    //     hashChunks(container.chunks),
    //     computeHash(container.chunks)
    // ]);
    const hash = await computeHash(container.chunks);
    const {data} = await checkRequest(hash);
    if (data.exits) {
        alert('文件已存在！');
        reset();
        return;
    }
    const t1 = new Date();
    await uploadChunks();
    const res = await mergeFileRequest(
        container.file.name,
        container.chunks.length,
        SIZE,
        hash
    );
    const t = Date.now() - t1;
    cost.textContent = t+'ms';
    fileURL.href = res.data.fileURL;
    fileURL.textContent = container.file.name;
    alert('上传成功！');
    reset();
}
function createFileChunk(file, size = SIZE) {
    const fileChunkList = [];
    let cur = 0;
    while (cur < file.size) {
        fileChunkList.push({file: file.slice(cur, cur + size)});
        cur += size;
    }
    return fileChunkList;
}

function getProtoList(obj) {
    const protoList = [];
    while ((obj = Object.getPrototypeOf(obj))) {
        protoList.push(obj);
    }
    return protoList;
}

const mergeFileRequest = async (filename, length, size, hash) => {
    return axios.post('/merge', {filename, length, size, hash});
};
